#include <my_header.h>


// 设置文件类型和权限字符串
void set_type_mode(mode_t mode, char *mode_str)
{
    // 根据文件类型设置字符串的第一个字符
    // S_IFMT 是一个位掩码，将st_mode成员与 S_IFMT 
    // 进行位与操作，就能够得到代表文件类型的值。
    switch (mode & S_IFMT) {
    case S_IFBLK:  mode_str[0] = 'b'; break; // 块设备
    case S_IFCHR:  mode_str[0] = 'c'; break; // 字符设备
    case S_IFDIR:  mode_str[0] = 'd'; break; // 目录
    case S_IFIFO:  mode_str[0] = 'p'; break; // 管道
    case S_IFLNK:  mode_str[0] = 'l'; break; // 链接
    case S_IFREG:  mode_str[0] = '-'; break; // 常规文件
    case S_IFSOCK: mode_str[0] = 's'; break; // 套接字
    default:       mode_str[0] = '?'; break; // 未知类型
    }

    // 根据文件权限设置字符串后续字符
    // 设置拥有者权限
    mode_str[1] = (mode & 0400) ? 'r' : '-';
    mode_str[2] = (mode & 0200) ? 'w' : '-';
    mode_str[3] = (mode & 0100) ? 'x' : '-';
    // 设置拥有者组的权限
    mode_str[4] = (mode & 0040) ? 'r' : '-';
    mode_str[5] = (mode & 0020) ? 'w' : '-';
    mode_str[6] = (mode & 0010) ? 'x' : '-';
    // 设置其他人的权限
    mode_str[7] = (mode & 0004) ? 'r' : '-';
    mode_str[8] = (mode & 0002) ? 'w' : '-';
    mode_str[9] = (mode & 0001) ? 'x' : '-';
    mode_str[10] = '\0';    // 确保字符串以null结尾

}

// 获取格式化的时间字符串
void get_time_str(time_t mtime, char *time_str)
{
    // 月份的缩写的字符二维数组(字符串数组)
    char mon_arr[][10]=
    {
        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
            //[10] 表示每个字符串的最大长度为9个字符，外加一
            //个结束符 \0。虽然实际使用中，月份的英文缩写最
            //长为3个字符，但预留10个字符的空间可以防止未来的
            //扩展或操作时发生溢出。]       
    };
    struct tm *tm_val = localtime(&mtime);  //获取tm结构体指针
    sprintf(time_str,
            "%s %02d %02d:%02d",
            mon_arr[tm_val->tm_mon],
            tm_val->tm_mday,
            tm_val->tm_hour,
            tm_val->tm_min);
}


/* Usage: ./7_result_myls_al pathname */
int main(int argc, char *argv[]){                                  
    char *dir_name; // 存储目录名的指针
    if(argc == 1)
    {
        // 命令行参数没有提供要打印的目录，打印当前目录
        dir_name = "."; 
    }
    else if(argc == 2)
    {
        // 否则用提供的命令行参数作为待打印的目标
        dir_name = argv[1];
    }else{
        fprintf(stderr, "argus num error!\n");
        exit(EXIT_FAILURE);
    }

    DIR* dirp = opendir(dir_name); // 打开指定的目录
    ERROR_CHECK(dirp, NULL, "opendir");



    // 改变工作目录到指定目录
    int ret = chdir(dir_name);
    ERROR_CHECK(ret, -1, "chdir");

    struct dirent* pdirent;
    // 遍历目录项
    while ((pdirent = readdir(dirp)) != NULL) {
        struct stat stat_buf;
        // 获取目录项的详细信息
        int ret = stat(pdirent->d_name, &stat_buf);
        ERROR_CHECK(ret, -1, "stat");

        char mode_str[1024] = { 0 };    // 保存文件类型和权限信息
        set_type_mode(stat_buf.st_mode, mode_str); // 设置类型和权限    ？

        char time_str[1024] = { 0 }; // 保存格式化后的时间信息
        /*
           localtime需要传入时间戳描述
           所以这里传参时可以写stat_buf.st_mtime（宏定义，更简写）
           也可以写stat_buf.st_mtim.tv_sec（宏定义的原版代码）
           但不能直接写stat_buf.st_mtim
           原因上面已经讲过了！
           */ 
        get_time_str(stat_buf.st_mtime, time_str);   // 获取时间字符串
        printf("%s %lu %s %s %4lu %s %s\n", 
               mode_str,                            // 文件类型与权限
               stat_buf.st_nlink,   // 硬链接数
               getpwuid(stat_buf.st_uid)->pw_name, // 拥有者名
               getgrgid(stat_buf.st_gid)->gr_name,  // 拥有者组名
               stat_buf.st_size,                    // 文件大小
               time_str,                            // 最后修改时间字符串
               pdirent->d_name); 

    }
    // 关闭目录
    closedir(dirp);
    return 0;
}


